#ifndef _MALICE_CODEGEN_H_
#define _MALICE_CODEGEN_H_

#include <vector>
#include <tr1/unordered_map>

// for BOOST_JOIN
#include <boost/config.hpp>

#include <MAlice/MAlice.h>

#include <llvm/PassManager.h>
#include <llvm/Module.h>

class MAliceDriver;


namespace MAlice
{

class Ast;
class ExpressionAst;
class Codegen;
typedef IRBuilder<> Builder;

class AutoReverse
{
private:
	BasicBlock* mBB;
	Codegen * mG;

public:
	AutoReverse(Codegen* g);
	~AutoReverse();
};

#define MALICE_AUTO_REVERSE(CODEGEN)                                          \
	::MAlice::AutoReverse                                                     \
	 BOOST_JOIN(BOOST_JOIN(BOOST_JOIN(MALICE_UID,__COUNTER__), __), __LINE__) \
	(CODEGEN)

class Codegen
{
public:
	typedef unordered_map<string, Value*> ScopeType;

private:
	Function* mFunction;
	Module * mModule;
	Builder * mBuilder;
	LLVMContext mContext; ///< LLVM context of the code generator
	vector<ScopeType> mVars;
	BasicBlock* mEntry;

	string getScopeError(const string& name);

public:
	enum Optimisation
	{
		NONE = 0, QUICK = 1, Default = 2, ULTRA= 3,
	};

	/**
	 * @return singleton instance of {#link Codegen}
	 * */
	static Codegen* get();

	Codegen();
	~Codegen();

	/** getters @{ */
	LLVMContext& getContext();
	Builder* getBuilder();
	Module* getModule();
	/** getters @} */

	/**
	 * @return Value* of a named type in the scope of the program or NULL if not found
	 * */
	Value* getVar(const string& name);
	void pushVar(const string& name, Value* value, Value* size = NULL);
	void pushScope();
	void popScope();

	Function* getFunction();
	void setFunction(Function* f);

	ExpressionAst* getDefaultValue(const string& type);

	Module* finalise(MAliceDriver& driver, Optimisation optimisation);
};

} // namespace MAlice

#endif /* _MALICE_CODEGEN_H_ */
